[#ftl]

[@define "agile._MetadataMixin"]		
"-chains-":{
	init:"after"
	,initDefaultMetadata:"after"
}	
,shortName:""
,_metadata:null
,containerContent:""
,readOnly:false
,captureKeyframe:false
,templateMap:{
	dojo:"<div {id} data-dojo-type='{dojoDeclaredClass}' data-dojo-props='{style-props}'>{innerWidets}</div>"
	,agile:"<div data-dojo-type='{declaredClass}' data-dojo-props='{style-props}'>{innerWidets}</div>"
}
,styleConstraints:{
	"left": lang.mixin({},agile.MetadataConstraint,{_readOnly:null,_ignore:false})
   ,"top": lang.mixin({},agile.MetadataConstraint,{_readOnly:null,_ignore:false})
   ,"right": lang.mixin({},agile.MetadataConstraint,{_readOnly:true,_ignore:false})
   ,"bottom": lang.mixin({},agile.MetadataConstraint,{_readOnly:true,_ignore:false})
   ,"width": lang.mixin({},agile.MetadataConstraint,{
	   readOnly:function(widget){
   		   return this._readOnly || (!widget.isResizable || widget.resizeAxis.indexOf("x") == -1);	   			
   	   }
	   ,onValueChange:function(ownerWidget,name,htmlName,value){
		   if(ownerWidget.delegate){
				style.set(ownerWidget.delegate.domNode,name,value);
			}
		   //TODO: apply style to getStyleNodes()
	   }
   	})
   ,"min-width": lang.mixin({},agile.MetadataConstraint,{_readOnly:true})
   ,"max-width": lang.mixin({},agile.MetadataConstraint,{_readOnly:true})
   ,"height":  lang.mixin({},agile.MetadataConstraint,{
	   readOnly:function(widget){
   		   return this._readOnly || (!widget.isResizable || widget.resizeAxis.indexOf("y") == -1);	   			
   	   }
   		,onValueChange:function(ownerWidget,name,htmlName,value){
		   if(ownerWidget.delegate){
			   style.set(ownerWidget.delegate.domNode,name,value);
		   }
   		}
   	})
   ,"min-height": lang.mixin({},agile.MetadataConstraint,{_readOnly:true})
   ,"max-height": lang.mixin({},agile.MetadataConstraint,{_readOnly:true})
   ,"z-index": lang.mixin({},agile.MetadataConstraint,{_readOnly:true,_ignore:false})
   ,"position":lang.mixin({},agile.MetadataConstraint,{_readOnly:true})
   ,"padding-top":lang.mixin({},agile.MetadataConstraint,{_readOnly:false})
   ,"padding-right":lang.mixin({},agile.MetadataConstraint,{_readOnly:false})
   ,"padding-bottom":lang.mixin({},agile.MetadataConstraint,{_readOnly:false})
   ,"padding-left":lang.mixin({},agile.MetadataConstraint,{_readOnly:false})
   ,"class": lang.mixin({},agile.MetadataConstraint,{_readOnly:false,ignore:function(ownerWidget,sourceType){ return this._ignore || sourceType!="agile";}})
   //,"keyframes": lang.mixin({},agile.MetadataConstraint,{_readOnly:false,ignore:function(ownerWidget,sourceType){ return sourceType!="agile";}})
   ,"*": lang.mixin({},agile.MetadataConstraint,{exceptions:"",_readOnly:true,_hidden:true,_ignore:false,ignore:function(ownerWidget,sourceType,name){}})
   ,"animation-play-state":lang.mixin({},agile.MetadataConstraint,{ignore:function(ownerWidget,sourceType){ return this._ignore || sourceType!="agile";}})
}
,events:{
	"onClick":lang.mixin({},agile.EventMetadata,{
		args:"event",_ignore:true,remark:agile.i18n("click_evt_remark",null,"Event triggered when widget is clicked")
	})
	,"onDblClick":lang.mixin({},agile.EventMetadata,{
		args:"event",_ignore:true,remark:agile.i18n("dblclick_evt_remark",null,"Event triggered when widget is clicked twice")
	})
	,"onKeyPress":lang.mixin({},agile.EventMetadata,{
		args:"event",_ignore:true,remark:agile.i18n("keypress_evt_remark",null,"Event triggered when key is pressed on the widget")	
	})
	,"onChange":lang.mixin({},agile.EventMetadata,{
		args:"value",_ignore:true,remark:agile.i18n("change_evt_remark",null,"Event triggered when the value of widget is changed")
	})
	,"onLoad":lang.mixin({},agile.EventMetadata,{
		args:"",_ignore:true,remark:agile.i18n("load_evt_remark",null,"Event triggered when the widget is loaded")
	})
}
,attributes:{		
	comment: lang.mixin({},agile.MultiTextMetadata ,{label:agile.i18n("comment",null,true)})
	,template:lang.mixin({},agile.BooleanMetadata ,{_ignore:true,_hidden:true})//TODO:find out where it comes
	,wid: lang.mixin({},agile.TextMetadata ,{
		label:agile.i18n("wid",null,"Widget ID")
		,_readOnly:true,_hidden:true,_ignore:false
		,ignore:function(widget,sourceType){
			return this._ignore || sourceType == "dojo";
		}
	})
	,layer:lang.mixin({},agile.NumberMetadata ,{
		label:agile.i18n("layer",null,"layer")
//		,_readOnly:true,_hidden:true,_ignore:false
		,ignore:function(widget,sourceType){
			return this._ignore || sourceType == "dojo";
		}
		,onValueChange:function(ownerWidget,name,value){
		   if(ownerWidget){
			   style.set(ownerWidget.domNode,{zIndex:value});
			   ownerWidget.metadata("style.z-index",value);
		   }
		}
	})
}
,properties:function(){
	//summary:lookup the widget's properties to show on the MetadataPropertiesEditorPane
	var e = {};
	var attributes = this.attributes;
	for(var name in attributes){
		if(name.indexOf("style") == 0){
			
		}else if(name.indexOf("event") == 0){
			
		}else{
			e[name] = attributes[name];
		}
	}
	return e;
}
,preamble:function(params, node){
	
//	console.log(this.declaredClass,"preamble:params:",params,",node:",node)
	
	//this.inherited(arguments);
	if(this._metadata == null){
		this._metadata = {};	
	}
	this.dimension = {left:0,top:0,height:0,width:0,padding:0,margin:0};
	
	this._extractDefaultParameterValue();
	//return [params, node];
}
,_getDelegatePrototype:function(){
	return null;
}
,_extractDefaultParameterValue:function(){
	//summary:extract the default parameter value which will be ignored when serialization	
	//only execute once for all instances	
	var prototype = this.constructor.prototype;
	
	if(!prototype[this.declaredClass]){
		prototype[this.declaredClass] = {};
	}
	
	if(!prototype[this.declaredClass].__extractDefaultParameterValue){
		//console.log(this.declaredClass,"_extractDefaultParameterValue....");
		var _delegatePrototype = this._getDelegatePrototype(); 
		if(_delegatePrototype){
			for(var name in prototype.attributes){
//				console.log("##########name=",name);
				if(!prototype.attributes[name].ignore(this,"dojo")){
					prototype.attributes[name]["defaultValue"] = lang.getObject(name,false,_delegatePrototype);
				}
			}
		}
		this._extractDefaultParameterValueOverride(prototype);
		
		var attributes = prototype.attributes;		
		for(var name in attributes){			
			if(!attributes[name].name){
				attributes[name].name = name;
			}
			if(!attributes[name].label){
				attributes[name].label = name;
			}
			if(attributes[name].label.indexOf("i18n_") == 0){
				var key = attributes[name].label.replace("i18n_","");
				attributes[name].label = agile.i18n(key,null,true);
				attributes[name].tooltip = agile.i18nTooltip(key,null,true);
			}			
		}
		
		var events = prototype.events;
		for(var name in events){			
			var cfg = events[name];
			if(!cfg.name){
				cfg.name = name;
				cfg.editorClass = name + "EventEditor";
				if(!cfg.label) {
					var label = name.replace("on","");
					cfg.label = agile.i18n(label,null,label);
				}
//				console.log("###########",cfg);
				cfg.templateString = lang.replace(cfg.templateString,cfg);
			}
		}
		
		prototype[this.declaredClass].__extractDefaultParameterValue = true;
	}	
}
,_extractDefaultParameterValueOverride:function(prototype){
	
}
,constructor:function(params, node){
	//console.log(this.declaredClass,"##constructor params=",params);
	this.mixinMetadata(params, node);	
	
	this.own(
		aspect.after(this,"postMixInProperties",lang.hitch(this,"adviseTemplateString"))
		,aspect.after(this,"postCreate",lang.hitch(this,"applyParameterStyle"))
		,aspect.after(this,"startup",lang.hitch(this,"init"))
	);	
}
,adviseTemplateString:function(){
	if(this.templateString){
		this.templateString = lang.replace(this.templateString,lang.mixin(agile.i18nValues(this.templateString,this),this._metadata));
//		console.log(this.declaredClass,"##templateString=",this.templateString);	
	}	
}
,init:function(){
	if(this._initMetadataMixin){
		return true;
	}
	this._initMetadataMixin = true;
	
	this.shortName = this.widgetName;
//	console.log(this.declaredClass,"[agile._MetadataMixin]init....");
	
	//the mask <div/> used to protect the widget
	this.mask = construct.create("div",{innerHTML:""});
	construct.place(this.mask,this.domNode,"last");
	style.set(this.mask,{left:"0px",top:"0px",position:"absolute"});
}
,_setReadOnlyAttr:function(readOnly){
	this.readOnly = readOnly;
	this.protect(readOnly);
}
,_setCaptureKeyframeAttr:function(value){
	this.captureKeyframe = value;
}
,protect:function(readOnly){
	//summary:protect the widget and its children from being changed
	if(readOnly){
		var maxIndex = 0;
		if(this._children){//for container
			array.forEach(this._children,function(child){			
				var idx = child.style1("z-index");
				if(maxIndex < idx){
					maxIndex = idx;
				}				
			},this);
			maxIndex++;
			
			array.forEach(this._children,function(child){			
				var idx = child.style1("z-index");
				child.style1("z-index",idx - maxIndex);				
			},this);
			
			this.childMaxZIndex = -1;
		}
		
		style.set(this.mask,{width:"100%",height:"100%",zIndex:1,opacity:0.3,backgroundColor:"#fafafa"});
	}
	else{
		style.set(this.mask,{width:"0%",height:"0%"});
	}
}

,getDescription:function(){
	return this.shortName;
}
,adviseState:function(param){
	return false;		
}
,adviseContextState:function(param){
	var applicable = false;
	switch(param){
		case "Copy":
		case "Cut":
		case "Delete":
			applicable = this._selected;
		
	}
	return applicable;		
}
,mixinMetadata:function(params, node){
//	console.log(this.declaredClass,"[_MetadataMixin:mixinMetadata]params=",params);
	this.metadata("dojoDeclaredClass","");
	this.initDefaultMetadata();
//TODO:move from layoutWidget 
//	if(node && node.innerHTML != ""){
//		this.containerContent = node.innerHTML;
//		construct.empty(node);		
//	}
//	console.log(this.declaredClass,"[_MetadataMixin2]mixinMetadata....containerContent=",this.containerContent);

	if(params){
//		var keyValue,htmlStyle = props.replace(/(.*)style:"(.*)"(.*)/,"$2").replace(/(-webkit-)|(-moz-)|(-ms-)|(-o-)|(!important)/g,"");
//		console.log(this.declaredClass,"[_MetadataMixin:mixinMetadata]htmlStyle=",params.style);
		if(params.style){
			var keyValue, paramStyle = params.style.replace(/(-webkit-)|(-moz-)|(-ms-)|(-o-)|(!important)/g,"");
			//store the style to metadata 
			array.forEach(paramStyle.split(";"),function(c){
				if(c){
					var processed = false;
					keyValue = c.split(":");
					[#list ["padding","margin"] as attr]
					if(keyValue[0] == "${attr}"){
						var values = keyValue[1].trim().split(" ");
						if(values[0] == undefined){
						//nothing to do	
						}else if(values[1] == undefined){
							this.metadata("style.${attr}-top",values[0]);
							this.metadata("style.${attr}-right",values[0]);
							this.metadata("style.${attr}-bottom",values[0]);
							this.metadata("style.${attr}-left",values[0]);
						}else if(values[2] == undefined){
							this.metadata("style.${attr}-top",values[0]);
							this.metadata("style.${attr}-right",values[1]);
							this.metadata("style.${attr}-bottom",values[0]);
							this.metadata("style.${attr}-left",values[1]);
						}else if(values[3] == undefined){
							this.metadata("style.${attr}-top",values[0]);
							this.metadata("style.${attr}-right",values[1]);
							this.metadata("style.${attr}-bottom",values[2]);
							this.metadata("style.${attr}-left",values[1]);
						}else{
							this.metadata("style.${attr}-top",values[0]);
							this.metadata("style.${attr}-right",values[1]);
							this.metadata("style.${attr}-bottom",values[2]);
							this.metadata("style.${attr}-left",values[3]);
						}
						//this.metadata("style.${attr}",null);
						processed = true;
					}
					[/#list]
					
					 if(keyValue[0] == "border"){				 
						array.forEach(["top","right","bottom","left"],lang.hitch(this,function(edge){
							this.metadata("style.border-" + edge,keyValue[1]?keyValue[1]:"");
						}));
						processed = true;
					 } 
										
					if(!processed){					
						this.metadata("style." + keyValue[0],keyValue[1]);
					}
				}
			},this);
			
		}
		//clear the style
		delete params.style;			

		if(params.event){
			this.metadata("event",params.event);
		}
		delete params.event;
		
		//store other parameters
		lang.mixin(this.metadata("parameters"),params);

		this.extractNodeParams(node);
	}			
//}
}
,extractNodeParams:function(node){
	//summary:extract parameters from the src node, e.g. parameters rendered as inner HTML code
}
,initDefaultMetadata:function(){
	//summary:set the widget's default parameters which can be overridden by the params	
	//1. default value from prototype
	//set the parameter value to the prototype's default value 
//	console.log(this.declaredClass,"[_MetadataMixin]initDefaultMetadata....");
	for(var name in this.attributes){
//		console.log(this.declaredClass,name,"defaultValue=",this.attributes[name]["defaultValue"])
		if(!this.attributes[name].ignore(this,"dojo")){
			if(this.metadata("parameters."+name) == undefined && this.attributes[name]["defaultValue"] != undefined){
				this.metadata("parameters."+name,this.attributes[name]["defaultValue"]);
			}
		}
	}		
}
,applyParameterStyle:function(force){	
	//style.set(this.domNode,this.metadata("style"));
	if(this._applyParameterStyle && !force){
		return;
	}
	this._applyParameterStyle = true;
	
	//apply css style to nodes
	var htmlStyle = this.metadata("style");		
//	console.log(this.declaredClass,"[_MetadataMixin:applyParameterStyle]htmlStyle=",htmlStyle);
	if(htmlStyle){			
		var styleRule = {};
		for(var e in htmlStyle){
			if("position left top class".indexOf(e) == -1){//exclude these attributes
				//styleRule += ";"+agile.adviseCssName4Html(e) +":" + htmlStyle[e];
				styleRule[agile.adviseCssName4Js(e)] = htmlStyle[e];
			}
		}		
//		console.log(this.declaredClass,"styleRule=",styleRule)
		var nodes = this.getStyleNodes();
		for(var i = nodes.length-1; i >= 0; i--){
			if(nodes[i]){
				//nodes[i].style.cssText += styleRule;
				style.set(nodes[i],styleRule);
			}
		}
		//set the initial position for the widget
		array.forEach(["position","left","top"],function(e){
			if(htmlStyle[e]){
				style.set(this.domNode,e,htmlStyle[e]);
			}
		},this);
		//style.set(this.domNode,{position:htmlStyle.position,left:htmlStyle.left,top:htmlStyle.top});	
		//apply the css class to nodes
		if(htmlStyle["class"]){
			//TODO:split into array??
			var cssClass = htmlStyle["class"]; 
			array.forEach(nodes,function(node){
				if(!domClass.contains(node,cssClass)){
					domClass.add(node,cssClass);
				}
			});	
		}
		
		//clear animation to make style editable
		if(htmlStyle["animation"]){
			array.forEach(nodes,function(node){
				style.set(node,agile.adviseCssName4Js("animation"),"");
			});
		}
			
	}
	//only apply animation rule for non-empty keyframes
//	var keyframes = this.metadata("parameters.keyframes");
//	if(keyframes){
//		var _keyframes = lang.clone(keyframes);		
//		for(var name in _keyframes){
//			var isEmpty = true;
//			for(var e in _keyframes[name]){
//				_keyframes[name][e] = agile.adviseCssRules(_keyframes[name][e]);
//				isEmpty = false;
//			}
//			if(!isEmpty){
//				agile.replaceKeyFrameRule(name,_keyframes[name]);
//			}
//		}		
//		//pause animation for prototype		
//		var htmlName = "animation-play-state", jsName = agile.adviseCssName4Js(htmlName);		
//		array.forEach(this.getStyleNodes(htmlName),function(node){
//			style.set(node,jsName,"paused");
//		});
//	}
}
,postMixInProperties:function(){
//	this.inherited(arguments);
//	this.templateString = lang.replace(this.templateString,lang.mixin(agile.i18nValues(this.templateString,this),this._metadata));				
	//this.own(aspect.before(this,"postCreate",lang.hitch(this,"applyParameterStyle")));
}
,getTemplateParameters:function(params){
	var sourceType = params.sourceType;
	
	var templateParameters = {
		innerWidets:""
		,widgetName : this.metadata("widgetName")
		,agileWidgetName: this.metadata("agileWidgetName")
		,parameters:""
		//,'dojo-parameters':null
		,'html-parameters':null
		,'html-style':null
		,"style-props":""
		,style:this.metadata("style")
		,props:""
		,id:""
		,declaredClass:this.declaredClass
		,dojoDeclaredClass:this.metadata("dojoDeclaredClass").replace(/\./g,"/")
	};
	var template = this.templateMap[sourceType];
	
	if(template.indexOf("{style-props}") != -1){
		var styleStr = this.serializeStyle2(sourceType,null);
		var propsStr = this.serializeParameters2({sourceType:sourceType,parameterStyle:"dojo",ignoreParams:params.ignoreParams});
		if(styleStr == "" || propsStr == ""){
			templateParameters["style-props"] = styleStr + propsStr;
		}else{
			templateParameters["style-props"] = styleStr +"," + propsStr;
		}
	}
	if(template.indexOf("{props}") != -1){
		templateParameters["props"] = this.serializeParameters2({sourceType:sourceType,parameterStyle:"dojo",ignoreParams:params.ignoreParams});
	}
//	//console.log(this.declaredClass,"[_Component]getTemplateParameters...",arguments,"template=",template);
//	//serialize style in html format
	if(template.indexOf("{html-style}") != -1){
		templateParameters["html-style"] = this.serializeStyle2(sourceType,"html");
	}
	if(template.indexOf("{dojo-style}") != -1){
		templateParameters["dojo-style"] = this.serializeStyle2(sourceType,null);
	}
//	if(template.indexOf("{style}") != -1){
//		templateParameters.style = this.serializeStyle2(sourceType,null);
//	}
//	
	if(template.indexOf("{html-parameters}") != -1){
		templateParameters['html-parameters'] = this.serializeParameters2({sourceType:sourceType,parameterStyle:"html",ignoreParams:params.ignoreParams});
	}
//	if(template.indexOf("{dojo-parameters}") != -1){
//		templateParameters['dojo-parameters']  = this.serializeParameters2(sourceType,"dojo");					
//	}
////	if(template.indexOf("{parameters}") != -1){
////		var p = this.serializeParameters2(sourceType,null);
////		if(p == "" || p == "{}"){
////			templateParameters['parameters'] = "";
////		}else{
////			templateParameters['parameters'] = (templateParameters.style?",":"") +"parameters:"+p;	
////		}			
////	}
//	//console.log(this.declaredClass,"[_Component]getTemplateParameters...styleStr=",styleStr," sourceType=",sourceType,"  parameterStr=",parameterStr);
	templateParameters.id = this.serializeId(sourceType);
	//templateParameters.innerWidets = this.serializeInnerHtml("html",sourceType);

	//serialize events
	var eventHandlers = this.serializeEvent(this.metadata("event"),sourceType);
	if(eventHandlers){
		if(sourceType == "dojo"){
			templateParameters.innerWidets += eventHandlers;
		}else{
			if(templateParameters["style-props"]){
				templateParameters["style-props"] += "," + eventHandlers;
			}else{
				templateParameters["style-props"] = eventHandlers;
			}
		}
	}
	if(params.cascade){
		templateParameters.innerWidets += this.getInnerWidgetTemplate(params);
	}
	////console.log(this.declaredClass,"agile._Component:getTemplateParameters() is called",templateParameters);
	return templateParameters;
}
,toStyle:function(){
	//generate the CSS styles
	var style = "";
	
	var keyframes = this.metadata("parameters.keyframes");
	if(keyframes){
		var _keyframes = lang.clone(keyframes);
		for(var name in _keyframes){
			for(var e in _keyframes[name]){
				_keyframes[name][e] = agile.adviseCssRules(_keyframes[name][e]);
			}
			style += agile.keyframes2String(name,_keyframes[name]);
		}
//		console.log("keyframe:",style);
	}
	
	var pseudo = this.metadata("parameters.pseudo");
	if(pseudo){
		var pseudoClassName = this.metadata("pseudoClass");			
		for(var e in pseudo){
			if(pseudo[e]){
				style += "\n." + pseudoClassName + ":" +e.replace(/_/g,"-") + "{" + agile.adviseCssRules(pseudo[e]) + "}";
			}					
		}
	}
			
	var containerNode = this.getContainerNode();
	if(containerNode){
		query(">.agileComponent",containerNode).forEach(function(node){
			var childWidget = registry.byNode(node);
			style += childWidget.toStyle();
		},this);
	}
	
	return style;
}
[@method "getStyleNodes"/]
,getStyleNodes:function(htmlName){
	var styleNodes = [];
	if(htmlName == "left" || htmlName == "top"){
		styleNodes.push(this.domNode);
	}else if(htmlName == "height" || htmlName == "width"){
		if(this.isResizable){
			styleNodes.push(this.getResizableTargetContainer());					
		}else{
			styleNodes.push(this.domNode);
		}
	}else if(this.delegate){
		styleNodes.push(this.delegate.domNode);
	}else{
		styleNodes.push(this.domNode);
	}
	
	return styleNodes;
}
,createId:function(){
	return 'w' + (new Date().getTime())  + "1234567890".substring(Math.floor(Math.random()*10));;
}
,serializeId:function(sourceType){
	//summary:serialize the id attribute if required, format: id="xxxxx"
	return "id=\"" + this.metadata("id") +"\"";
//	return "";
}
,serializeEvent:function(eventMetadata,sourceType){	
	var events = "";	
	if(eventMetadata){
		if(sourceType == "dojo"){
			for(var event in eventMetadata){	
				events += this.events[event].serialize(event,eventMetadata[event],sourceType);
			}
		}else{
			events =  "event:" + json.stringify(eventMetadata);
		}
	}
//	console.log("serializeEvent## metadata:",eventMetadata,",sourceType:",sourceType,",events:",events);
	return events;
}
,serializeStyle:function(styleValues,sourceType,parameterStyle){
	var styleStr = "";
	if(styleValues){
		//merge the attributes
		[#list ["padding","margin"] as e]
		var ${e} = "";
		array.forEach(["${e}-top","${e}-right","${e}-bottom","${e}-left"],function(e){
			${e} += " " + (styleValues[e]?styleValues[e]:"0px");
			delete styleValues[e];
		});
		if(${e}.replace(/0px/g,"").trim() != ""){
			styleValues["${e}"] = ${e}.trim();
		}
		[/#list]
		
		 array.forEach(["top","right","bottom","left"],function(edge){
			 if(styleValues["border-" + edge] == "0px"){
				 delete styleValues["border-" + edge];	 
			 }
		 });
		 
		if(sourceType == "dojo"){
			for(var e in styleValues){
				if(styleValues[e]){
					styleStr += agile.adviseCssName4Html(e) +":" + styleValues[e] +";"
				}
			}							
		}else{			
			for(var e in styleValues){
				if(styleValues[e]){
					styleStr += e +":" + styleValues[e] +";"
				}
			}
		}		
		styleStr = styleStr.substring(0,styleStr.lastIndexOf(";"));
	}
	if(styleStr != ""){
		if(parameterStyle == "html"){
			styleStr = "style=\""+styleStr+"\"";
		}else{
			styleStr = "\"style\":\""+styleStr+"\"";
		}
	}
		
	var cssClass = "";
	if(this.metadata("pseudoClass")){
		cssClass = this.metadata("pseudoClass") +" ";
	}
	if(this.metadata("style.class")){
		cssClass += this.metadata("style.class");
	}
	if(cssClass != ""){
		if(parameterStyle == "html"){
			styleStr += " class=\"" + cssClass +"\"";
		}else{
			if(styleStr){
				styleStr += ",\"class\":\"" + cssClass +"\"";
			}else{
				styleStr += "\"class\":\"" + cssClass +"\"";
			}
		}
	}		
	return styleStr;
}
,serializeStyle1:function(styleValues,sourceType,parameterStyle,scope){
	var styleStr = "";
	var styleConstraints = this.styleConstraints;
	
	if(styleConstraints["*"]){
		if(styleConstraints["*"].exceptions){
			for(var e in styleValues){
				if(styleConstraints["*"].ignore(this,sourceType,e)){
					delete styleValues[e];
				}
			}			
		}else{
			if(styleConstraints["*"].ignore(this,sourceType)){
				return styleStr;
			}
		}
	}
	//var styleValues = lang.mixin({},this.metadata("style"));
//	var styleValues = lang.clone(this.metadata("style"));
	for(var e in styleConstraints){
		//console.log(this.declaredClass,"serializeStyle2=",e,styleConstraints[e]);
		if(styleValues[e] == undefined ) continue;
		if(styleConstraints[e].ignore(this,sourceType)){			
			agile.setValue(e,null,styleValues);
		}else{
			if(styleConstraints[e].important(this,sourceType) && styleValues[e].indexOf("!important") == -1){
				agile.setValue(e,styleValues[e]+"!important",styleValues);
			}		
		}
	}
	if(scope){
		if(scope.type == "I"){//include only
			var _styleValues = {};
			for(var i = 0, length = scope.elements.length; i < length; i++){
				_styleValues[scope.elements[i]] = styleValues[scope.elements[i]];
			}
			styleValues = _styleValues;
		}else{//exclude 
			for(var i = 0, length = scope.elements.length; i < length; i++){
				delete styleValues[scope.elements[i]];
			}
		}		
	}
	return this.serializeStyle(styleValues,sourceType,parameterStyle);

}	
[@method "serializeStyle2"/]
,serializeStyle2:function(sourceType,parameterStyle,scope){
	return this.serializeStyle1(lang.mixin({},this.metadata("style")),sourceType,parameterStyle,scope);
}	
,serializeParameters2:function(params){
	
	var sourceType = params.sourceType, parameterStyle = params.parameterStyle,ignoreParams = params.ignoreParams;
	
	//filter parameters 
	var parameterValues = lang.mixin({},this.metadata("parameters"));
		
	var parameterConstraints = this.attributes;
	for(var e in parameterConstraints){
		if(parameterConstraints[e].ignore == undefined){
			console.warn(this.declaredClass,"[_Component]ignore(...) not found for parameter=",e,parameterConstraints[e]);
		}else if(parameterConstraints[e].ignore(this,sourceType) || parameterConstraints[e].defaultValue == parameterValues[e]){
			//agile.setValue(e,null,parameterValues);
			delete parameterValues[e];
		}else if(parameterConstraints[e].renderAsInnerHtml || parameterConstraints[e].renderAsOutterHtml){
			delete parameterValues[e];
		}
		//remove parameters to be ignored
		if(ignoreParams && ignoreParams[e]){
			delete parameterValues[e];
		}
		//TODO:fix me not to convert nested property value
//		if(e.indexOf("_") != -1){				
//			agile.setValue(e.replace(/_/g,"."),parameterValues[e],parameterValues);
//			delete parameterValues[e];
//		}
	}		
	delete parameterValues["style"];
	
	var parameterStr = "";
	if(parameterStyle == "html"){
		for(var e in parameterValues){
			if(e != "keyframes" && parameterValues[e]){
				parameterStr += e + "='" + parameterValues[e]+"' ";
			}
		}			
	}else if(parameterStyle == "dojo"){			
		parameterStr = this.serializeValue(parameterValues);
		parameterStr = parameterStr.substring(1,parameterStr.lastIndexOf("}"));
	}else{
		console.error("not supported parameterStyle:",parameterStyle);
	}

	return parameterStr;
}
,serializeValue:function(value){
	if(typeof value == "boolean"){
		return value;
	}else if(typeof value == "number"){
		return value;
	}else if(typeof value == "string"){//TODO:fix me for \n \r special characters
		return '"'+value.replace(/\n/g,"#").replace(/"/g,'\\"').replace(/'/g,"&#39;") +'"';
	}else if(value.constructor == Date){
		if(value.getYear() == 70){//format the time
			return '"'+value.toString().replace(/.*1970\s(\S+).*/,'T$1') +'"';
		}else{//format the date
			return '"'+locale.format(value,{formatLength:"medium",selector:"date",datePattern:"yyyy-MM-dd"}) +'"';
		}
	}else if(value.constructor == Array){
		var str = "";
		array.forEach(value,function(v){
			if(v != undefined){
				str += this.serializeValue(v) + ",";
			}
		},this);			
		return str == ""?"[]":"[" + str.substring(0,str.lastIndexOf(",")) + "]";			
	}else if(value.constructor == Object){
		var str = "";
//			console.log("##############value=",value);
		for(var e in value){
			//if(e == "constraints")console.error("##############value[e]=",e,value[e]);
			if(value[e] != undefined){//use strict for json
				str += '"'+e +'"' +":" + this.serializeValue(value[e]) + ",";
			}
		}
		return str == ""?"{}":"{" + str.substring(0,str.lastIndexOf(",")) + "}";			
	}
	else{
		console.error("serialization not support for ",typeof value,",value=",value);
		return null;
	}	
}
[@method "getInnerWidgetTemplate"/]
,getInnerWidgetTemplate:function(params){
	var innerWidets = "", sourceType = params.sourceType;
		
	var containerNode = this.getContainerNode();
//		//console.log(this.declaredClass,"agile._Component:getInnerWidgetTemplate() is called.....containerNode=",containerNode);
	if(containerNode != null){
		var childWidgets = new Array();
		query(">.agileComponent",containerNode).forEach(function(node){
			var childWidget = registry.byNode(node);
			//var order = childWidget.metadata(this.sortAxis == "y"?"style.top":"style.left");
			var order = childWidget.dimension[this.sortAxis == "y"?"top":"left"];
			
			if(!order){
				order = 0;
//				console.warn(this.declaredClass,"agile._Component:getInnerWidgetTemplate()","TODO");
			}
			while(childWidgets[order] != null){
				order++;
			}
			childWidgets[order] = childWidget;
			//templateParameters.innerWidets += childWidget.toHtml(cascade,indentCount+1,sourceType);
			////console.log(this.declaredClass,"agile._Component:getInnerWidgetTemplate()->childWidget",childWidget);
		},this);		
		
//			//console.log(this.declaredClass,"agile._Component:getInnerWidgetTemplate()->childWidgets",childWidgets);
		
		array.forEach(childWidgets,function(childWidget){
			if(childWidget){
				params.indentCount++;
				innerWidets += childWidget.toHtml(params);						
			}
		});
	}
//		//console.log(this.declaredClass,"agile._Component:getInnerWidgetTemplate() is called");
	
	var parameterValues = this.metadata("parameters");	
	var paramConstraint = this.attributes;
	for(var e in paramConstraint){
		if(paramConstraint[e].isRenderAsInnerHtml() && !paramConstraint[e].ignore(this,sourceType) && paramConstraint[e].defaultValue != parameterValues[e]){
			innerWidets += paramConstraint[e].toRenderAsInnerHtml(parameterValues[e],this,sourceType);
		}		
	}
	
	return innerWidets;
}
,toHtml:function(params){
//		console.warn(this.declaredClass,"####toHtml,parameters=",this.metadata("parameters"));
	var indent = this.indent(params.indentCount);
	var template = this.templateMap[params.sourceType];
//	console.log(this.declaredClass,"@@@",template,this.templateMap,",lang=",lang);
	template = lang.replace(template,this.getTemplateParameters(params));
//		//console.log(this.declaredClass,"agile._Component:toHtml() is called",template);
	return template;
}
,metadata:function(){
	//summary:TODO: migrated from Contained    	
	////console.log(this.declaredClass,"metadata:function",arguments);
	var name = arguments[0];
	var value = arguments[1];
	//no parameters
	if(arguments.length == 0){		
		return this._metadata;
	}
	//only name parameter		
	if(arguments.length == 1){
		return agile.getValue(name,this._metadata);
	}		
	//both parameters		
//	var beforeValue = agile.getValue(name,this._metadata);
	if(name == "style.left" || name == "style.top" || name == "style.width" || name == "style.height" || name.indexOf("style.padding") != -1){
		if(value == null){
			
		}else if(typeof value == "number"){
			
		}else if(typeof value == "string"){
			value = parseFloat(value.replace("px",""));	
		}
		if(value == null || isNaN(value)){
			agile.setValue(name,null,this._metadata);
			this.dimension[name.replace("style.","")] = 0;
		}else{
			agile.setValue(name,value+"px",this._metadata);			
			this.dimension[name.replace("style.","")] = value;
		}		
	}else{
		agile.setValue(name,value,this._metadata);
		if(name == "moveable" && this.moveableWidget){
			this.moveableWidget.delay = value?0:1000;
		}
	}
	//keep audits
//	var afterValue = agile.getValue(name,this._metadata);	 
//	agile.commit({target:this,property:name,beforeValue:beforeValue,afterValue:afterValue});	
}
,style1:function(){
	var name,value;
	
	if(arguments.length == 0){
		return this.metadata("style");
	}
	else if(arguments.length == 1){
		name = arguments[0];
		if(name == "left" || name == "top" || name == "width" || name == "height" || name == "padding" || name == "margin"){
			return this.dimension[name];
		}
		return this.metadata("style."+name);
	}
	else{
		name = arguments[0];
		value = arguments[1];
		
		if(name == "left" || name == "top" || name == "width" || name == "height" || name == "padding" || name == "margin"){				
			if(typeof value == "string"){
				value = parseFloat(value.replace("px",""));
			}
			this.dimension[name] = value;
			this.metadata("style."+name,value +"px");
			
			if(name == "left" || name == "top"){				
//				style.set(this.domNode,name,(value + this.metadata("anchor." + name)) +"px");
				for(var nodes = this.getStyleNodes(name), i = nodes.length - 1; i >= 0; i--){					
					style.set(nodes[i],name,(value + this.metadata("anchor." + name)) +"px");
				}
			} 
			else if(name == "width" || name == "height"){
				////console.log(this.declaredClass,name,"this.isResizable",this.isResizable,"this.getResizableTargetContainer()",this.getResizableTargetContainer());
//				if(this.isResizable){			
//					//style.set(this.targetResiableContainer,name,value);
//					style.set(this.getResizableTargetContainer(),name,value +"px");					
//				}else{
					//style.set(this.domNode,name,value +"px");
					for(var nodes = this.getStyleNodes(name), i = nodes.length - 1; i >= 0; i--){					
						style.set(nodes[i],name,value +"px");
					}
//				}
			} else if(name == "padding" || name == "spacing"){
				//style.set(this.domNode,name,value +"px");
				for(var nodes = this.getStyleNodes(name), i = nodes.length - 1; i >= 0; i--){					
					style.set(nodes[i],name,value +"px");
				}
			}
		}
		else{
			//style.set(this.domNode,agile.adviseCssName4Js(name),value);
			var jsName = agile.adviseCssName4Js(name);
			for(var nodes = this.getStyleNodes(name), i = nodes.length - 1; i >= 0; i--){					
				style.set(nodes[i],jsName,value);
			}
			
			this.metadata("style."+name,value+"");
		}	
	}
}
,indent:function(/*integer*/count){
	var value = "";
	if(count){
		for(var i = 0; i < count; i++){
			value += "\t";
		}
	}
	return value;
}
,preventDelete:function(widget){
	widget.own(aspect.after(widget,"edit",lang.hitch(this,function(){
		var editWidget = widget.wrapperWidget.editWidget;
		if(!editWidget._initialized){
			on(editWidget, "keyDown",function(evt){
				evt.stopPropagation();
			});
			on(editWidget, "keyUp",function(evt){
				evt.stopPropagation();
			});
			editWidget._initialized = true;
		}								
	})));
}
[/@]
